package Contracts;

import Exceptions.InvariantException;
import Exceptions.PostCondException;
import Exceptions.PreCondException;
import Impl.Bloc;
import Impl.Terrain;
import Impl.Type;
import Services.BlocService;
import Services.TerrainService;


public class TerrainServiceContract extends TerrainServiceDecorator{

	public TerrainServiceContract(TerrainService terrainService) {
		super(terrainService);
	}
	
	public void checkInvariants(){
		if(! ((1<=getXJoueur()) && (getXJoueur()<=getSizeX())))
			throw new InvariantException("invariant numero 1");
		
		if(! ((1<=getYJoueur()) && (getYJoueur()<=getSizeY())))
			throw new InvariantException("invariant numero 2");
		
		if(! ((getNbPorte()==1) || (!isStarted())))
			throw new InvariantException("invariant numero 3");
		
		if(! (getNbDiamant()>=0) )
			throw new InvariantException("invariant numero 4");
		
		if(! (getNbJoueur()==1))
			throw new InvariantException("invariant numero 5");
		
		for(int x=1; x<=getSizeX(); x++){
			for(int y=1; y<=getSizeY(); y++){
				if(! (getTypeBloc(x,y)==getBloc(x, y).getType()))
					throw new InvariantException("invariant numero 6");
				if(! (getOldTypeBloc(x,y)==getBloc(x, y).getOldType()))
					throw new InvariantException("invariant numero 7");
			}
		}
	}

	// Constructeurs
	@Override
	public void clone(TerrainService terrain){
		
		super.clone(terrain);
		
		// Invariants
		checkInvariants();
		
		// Postconditions
		if(! (getXJoueur()==terrain.getXJoueur()))
			throw new PostCondException("post clone 1");
		if(! (getYJoueur()==terrain.getYJoueur()))
			throw new PostCondException("post clone 2");
		if(! (getNbJoueur()==terrain.getNbJoueur()))
			throw new PostCondException("post clone 3");
		if(! (getNbDiamant()==terrain.getNbDiamant()))
			throw new PostCondException("post clone 4");
		if(! (getNbPorte()==terrain.getNbPorte()))
			throw new PostCondException("post clone 5");
		if(! (getSizeX()==terrain.getSizeX()))
			throw new PostCondException("post clone 6");
		if(! (getSizeY()==terrain.getSizeY()))
			throw new PostCondException("post clone 7");
		if(!(isStarted()==terrain.isStarted()))
			throw new PostCondException("post clone 8");
		
		for(int x=1; x<=getSizeX(); x++){
			for(int y=1; y<=getSizeY(); y++){
				if(! getBloc(x,y).equals(terrain.getBloc(x, y)))
					throw new PostCondException("post clone 9");
			}
		}
		
		for(int x=1; x<=getSizeX(); x++){
			for(int y=1; y<=getSizeY(); y++){
				if(! (isChanged(x,y)==terrain.isChanged(x, y)))
					throw new PostCondException("post clone 10");
			}
		}
	}
	
	@Override
	public void init(int sizeX, int sizeY) {
		// Preconditions
		if(! (sizeX>1 && sizeY>1))
			throw new PreCondException("pre init 1");
		
		super.init(sizeX, sizeY);
		
		// Invariants
		checkInvariants();
		
		// Postconditions
		BlocService blocTest = new Bloc();
		blocTest.init(Type.Hero);
		if(! (getBloc(1,1).equals(blocTest)))
			throw new PostCondException("post init 1");
		
		blocTest.init(Type.Vide);
		for(int x=1; x<=getSizeX(); x++){
			for(int y=1; y<=getSizeY(); y++){
				if(x==1 && y==1) break;
				if(! getBloc(x,y).equals(blocTest))
					throw new PostCondException("post init 2");
			}
		}
		
		for(int x=1; x<=getSizeX(); x++){
			for(int y=1; y<=getSizeY(); y++){
				if(! (isChanged(x,y)==false))
					throw new PostCondException("post init 3");
			}
		}
		
		if(! (getXJoueur()==1))
			throw new PostCondException("post init 4");
		if(! (getYJoueur()==1))
			throw new PostCondException("post init 5");
		if(! (getNbJoueur()==1))
			throw new PostCondException("post init 6");
		if(! (getNbDiamant()==0))
			throw new PostCondException("post init 7");
		if(! (getNbPorte()==0))
			throw new PostCondException("post init 8");
		if(! (getSizeX()==sizeX))
			throw new PostCondException("post init 9");
		if(! (getSizeY()==sizeY))
			throw new PostCondException("post init 10");
		if(!(isStarted()==false))
			throw new PostCondException("post init 11");
	}
	
	public void init(String filename){
		super.init(filename);
		
		// Invariants
		checkInvariants();
	}

	// Observateurs
	@Override
	public int getSizeX() {
		return super.getSizeX();
	}

	@Override
	public int getSizeY() {
		return super.getSizeY();
	}

	@Override
	public BlocService getBloc(int x, int y) {
		return super.getBloc(x, y);
	}

	@Override
	public Type getTypeBloc(int x, int y) {
		return super.getTypeBloc(x, y);
	}

	@Override
	public Type getOldTypeBloc(int x, int y) {
		return super.getOldTypeBloc(x, y);
	}

	@Override
	public int getXJoueur() {
		return super.getXJoueur();
	}

	@Override
	public boolean isStarted(){
		return super.isStarted();
	}
	
	@Override
	public int getYJoueur() {
		return super.getYJoueur();
	}

	@Override
	public int getNbDiamant() {
		return super.getNbDiamant();
	}

	@Override
	public int getNbPorte() {
		return super.getNbPorte();
	}

	@Override
	public boolean isChanged(int x, int y){
		return super.isChanged(x, y);
	}
	
	@Override
	public void startTerrain(){
		// Preconditions
		if(! (!isStarted()))
			throw new PreCondException("pre startTerrain 1");
		if(! (getNbPorte()==1))
			throw new PreCondException("pre startTerrain 2");
		if(! (getNbDiamant()>0))
			throw new PreCondException("pre startTerrain 3");
		
		// AtPre
		TerrainService terrainAtPre = new Terrain();
		terrainAtPre.clone(getDelegate());
		
		// Invariants
		checkInvariants();
		
		super.startTerrain();
		
		// Invariants
		checkInvariants();
		
		// Postconditions
		if(! (getXJoueur()==terrainAtPre.getXJoueur()))
			throw new PostCondException("post 1 startTerrain");
		if(! (getYJoueur()==terrainAtPre.getYJoueur()))
			throw new PostCondException("post 2 startTerrain");
		if(! (getNbDiamant()==terrainAtPre.getNbDiamant()))
			throw new PostCondException("post 3 startTerrain");
		if(! (getNbPorte()==terrainAtPre.getNbPorte()))
			throw new PostCondException("post 4 startTerrain");
		if(! (getNbJoueur()==terrainAtPre.getNbJoueur()))
			throw new PostCondException("post 5 startTerrain");		
		if(!(isStarted()))
			throw new PostCondException("post 6 startTerrain");
		
		for(int i=1; i<=getSizeX(); i++)
			for(int j=1; j<=getSizeY(); j++)
				if(! isChanged(i, j)==terrainAtPre.isChanged(i, j))
					throw new PostCondException("post 7 startTerrain");

		for(int i=1; i<=getSizeX(); i++){
			for(int j=1; j<=getSizeY(); j++){
				if(! getBloc(i,j).equals(terrainAtPre.getBloc(i, j)))
					throw new PostCondException("post 8 startTerrain");
			}
		}
	}
	
	@Override
	public void addObject(int x, int y, Type type){
		// Preconditions
		if(!( !isStarted() ))
			throw new PreCondException("pre addObject 1");
		if(!( 1<=x && x<=getSizeX()))
			throw new PreCondException("pre addObject 2");
		if(!( 1<=y && y<=getSizeY()))
			throw new PreCondException("pre addObject 3");
		if(!(type!=Type.Hero && type!=Type.SortieOuverte))
			throw new PreCondException("pre addObject 4");
		if(!(getTypeBloc(x, y)==Type.Vide))
			throw new PreCondException("pre addObject 5");
		
		// AtPre
		TerrainService terrainAtPre = new Terrain();
		terrainAtPre.clone(getDelegate());
		
		// Invariants
		checkInvariants();
		
		super.addObject(x, y, type);

		// Invariants
		checkInvariants();
		
		// Postconditions
		if(! (getXJoueur()==terrainAtPre.getXJoueur()))
			throw new PostCondException("post 1 addObject");
		if(! (getYJoueur()==terrainAtPre.getYJoueur()))
			throw new PostCondException("post 2 addObject");
		if(! (getNbJoueur()==terrainAtPre.getNbJoueur()))
			throw new PostCondException("post 3 addObject");
		if(! (isStarted()==terrainAtPre.isStarted()))
			throw new PostCondException("post 4 addObject");
		
		if(type==Type.Diamant && !(getNbDiamant()==terrainAtPre.getNbDiamant()+1))
			throw new PostCondException("post 5 addObject");
		else if(type!=Type.Diamant && !(getNbDiamant()==terrainAtPre.getNbDiamant()))
			throw new PostCondException("post 6 addObject");
		
		if(type==Type.SortieFermee && !(getNbPorte()==terrainAtPre.getNbPorte()+1))
			throw new PostCondException("post 7 addObject");
		else if(type!=Type.SortieFermee && !(getNbPorte()==terrainAtPre.getNbPorte()))
			throw new PostCondException("post 8 addObject");
		
		for(int i=1; i<=getSizeX(); i++)
			for(int j=1; j<=getSizeY(); j++)
				if(! isChanged(i, j)==terrainAtPre.isChanged(i, j))
					throw new PostCondException("post 8 addObject");

		terrainAtPre.getBloc(x, y).ChangeType(type);
		for(int i=1; i<=getSizeX(); i++){
			for(int j=1; j<=getSizeY(); j++){
				if(! getBloc(i,j).equals(terrainAtPre.getBloc(i, j)))
					throw new PostCondException("post 9 addObject");
			}
		}
	}

	@Override
	public void openDoor() {
		// Preconditions
		if(! isStarted())
			throw new PreCondException("pre openDoor 1");
		if(!(getNbDiamant()==0))
			throw new PreCondException("pre openDoor 2");
		
		// Invariants
		checkInvariants();
		
		// AtPre
		TerrainService terrainAtPre = new Terrain();
		terrainAtPre.clone(getDelegate());
		
		super.openDoor();
		
		// Invariants
		checkInvariants();
		
		// Postconditions
		if(! (getXJoueur()==terrainAtPre.getXJoueur()))
			throw new PostCondException("post 1 openDoor");
		if(! (getYJoueur()==terrainAtPre.getYJoueur()))
			throw new PostCondException("post 2 openDoor");
		if(! (getNbDiamant()==terrainAtPre.getNbDiamant()))
			throw new PostCondException("post 3 openDoor");
		if(! (getNbPorte()==terrainAtPre.getNbPorte()))
			throw new PostCondException("post 4 openDoor");
		if(! (getNbJoueur()==terrainAtPre.getNbJoueur()))
			throw new PostCondException("post 5 openDoor");
		if(! (isStarted()==terrainAtPre.isStarted()))
			throw new PostCondException("post 6 openDoor");
		
		for(int i=1; i<=getSizeX(); i++)
			for(int j=1; j<=getSizeY(); j++)
				if(! isChanged(i, j)==terrainAtPre.isChanged(i, j))
					throw new PostCondException("post 7 openDoor");
		
		for(int i=1; i<=getSizeX(); i++){
			for(int j=1; j<=getSizeY(); j++){
				if(terrainAtPre.getTypeBloc(i,j)==Type.SortieFermee){
					terrainAtPre.getBloc(i, j).ChangeType(Type.SortieOuverte);
				}	
				if(! getBloc(i,j).equals(terrainAtPre.getBloc(i, j)))
					throw new PostCondException("post 9 openDoor");
			}
		}
	}
	
	@Override
	public void validate() {
		// Preconditions
		if(! isStarted())
			throw new PreCondException("pre validate 1");
		
		// atPre
		TerrainService terrainAtPre = new Terrain();
		terrainAtPre.clone(getDelegate());
		
		// Invariants
		checkInvariants();
		
		super.validate();
		
		// Invariants
		checkInvariants();
		
		// Postconditions
		if(!(getXJoueur()==terrainAtPre.getXJoueur()))
			throw new PostCondException("post 1 validate");
		if(!(getYJoueur()==terrainAtPre.getYJoueur()))
			throw new PostCondException("post 2 validate");
		if(!(getNbDiamant()==terrainAtPre.getNbDiamant()))
			throw new PostCondException("post 3 validate");
		if(!(getNbPorte()==terrainAtPre.getNbPorte()))
			throw new PostCondException("post 4 validate");
		if(!(getNbJoueur()==terrainAtPre.getNbJoueur()))
			throw new PostCondException("post 5 validate");
		if(! (isStarted()==terrainAtPre.isStarted()))
			throw new PostCondException("post 6 validate");
		
		for(int i=1; i<=getSizeX(); i++)
			for(int j=1; j<=getSizeY(); j++)
				if(! isChanged(i, j)==false)
					throw new PostCondException("post 7 validate");
		
		for(int i=1; i<=getSizeX(); i++)
			for(int j=1; j<=getSizeY(); j++)
				if(! getBloc(i,j).equals(terrainAtPre.getBloc(i, j)))
					throw new PostCondException("post 8 validate");
	}
	
	@Override
	public void putHero(int x, int y){
		// Preconditions
		if(! (0<=x && x<=getSizeX()+1))
			throw new PreCondException("pre 1 putHero");
		if(! (0<=y && y<=getSizeY()+1))
			throw new PreCondException("pre 2 putHero");
		
		// Invariants
		checkInvariants();
		
		// AtPre
		TerrainService terrainAtPre = new Terrain();
		terrainAtPre.clone(getDelegate());
		
		super.putHero(x, y);
		
		// Invariants
		checkInvariants();
		
		// Postconditions
		if(!(getNbJoueur()==terrainAtPre.getNbJoueur()))
			throw new PostCondException("post 1 putHero");
		if(!(getNbPorte()==terrainAtPre.getNbPorte()))
			throw new PostCondException("post 2 putHero");
		if(! (isStarted()==terrainAtPre.isStarted()))
			throw new PostCondException("post 3 putHero");
		
		if(x==0 && !(getXJoueur()==getSizeX()))
			throw new PostCondException("post 4 putHero");
		else if(x==getSizeX()+1 && !(getXJoueur()==1))
			throw new PostCondException("post 4 putHero");
		else if(x!=0 && x!=getSizeX()+1 && !(getXJoueur()==x))
			throw new PostCondException("post 4 putHero");
			
		if(y==0 && !(getYJoueur()==getSizeY()))
			throw new PostCondException("post 5 putHero");
		else if(y==getSizeY()+1 && !(getYJoueur()==1))
			throw new PostCondException("post 5 putHero");
		else if(y!=0 && y!=getSizeY()+1 && !(getYJoueur()==y))
			throw new PostCondException("post 5 putHero");
		
		
		if(terrainAtPre.getTypeBloc(x,y) == Type.Diamant){
			if(!(getNbDiamant()==terrainAtPre.getNbDiamant()-1))
				throw new PostCondException("post 6 putHero");
		}else{
			if(!(getNbDiamant()==terrainAtPre.getNbDiamant()))
				throw new PostCondException("post 6 putHero");
		}
		
		for(int i=1; i<=getSizeX(); i++)
			for(int j=1; j<=getSizeY(); j++)
				if(! isChanged(i, j)==terrainAtPre.isChanged(i, j))
					throw new PostCondException("post 7 putHero");
		
		terrainAtPre.getBloc(terrainAtPre.getXJoueur(), terrainAtPre.getYJoueur()).ChangeType(Type.Vide);
		terrainAtPre.getBloc(x, y).ChangeType(Type.Hero);
		
		for(int i=1; i<=getSizeX(); i++){
			for(int j=1; j<=getSizeY(); j++){
				if(! getBloc(i,j).equals(terrainAtPre.getBloc(i, j)))
					throw new PostCondException("post 8 validate");
			}
		}
	}
	
	@Override
	public void moveObject(int x, int y, int newx, int newy){
		// Pr�conditions
		if(! (getTypeBloc(x, y)==Type.Rocher || getTypeBloc(x, y)==Type.Diamant))
			throw new PreCondException("precond moveObject 1");
		if(! (getTypeBloc(newx, newy)==Type.Vide || getTypeBloc(newx, newy)==Type.Hero))
			throw new PreCondException("precond moveObject 2");
		if(! (!isChanged(x,y)))
			throw new PreCondException("precond moveObject 3");
		if(! isStarted())
			throw new PreCondException("precond moveObject 4");
		
		
		// AtPre
		TerrainService terrainAtPre = new Terrain();
		terrainAtPre.clone(getDelegate());
		Type typeAtPre = getTypeBloc(x,y);
		
		// Invariants
		checkInvariants();
		
		super.moveObject(x, y, newx, newy);
		
		// Invariants
		checkInvariants();
		
		// Postconditions
		if(! (getXJoueur()==terrainAtPre.getXJoueur()))
			throw new PostCondException("post 1 moveObject");
		if(! (getYJoueur()==terrainAtPre.getYJoueur()))
			throw new PostCondException("post 2 moveObject");
		if(! (getNbDiamant()==terrainAtPre.getNbDiamant()))
			throw new PostCondException("post 3 moveObject");
		if(! (getNbPorte()==terrainAtPre.getNbPorte()))
			throw new PostCondException("post 4 moveObject");
		if(! (getNbJoueur()==terrainAtPre.getNbJoueur()))
			throw new PostCondException("post 5 moveObject");
		if(! (isChanged(newx, newy)))
			throw new PostCondException("post 6 moveObject");
		if(! (isStarted()==terrainAtPre.isStarted()))
			throw new PostCondException("post 7 moveObject");
		
		BlocService blocNew = getBloc(newx, newy);
		BlocService bloc = getBloc(x, y);
		
		
		for(int i=1; i<=getSizeX(); i++){
			for(int j=1; j<=getSizeY(); j++){
				if(getBloc(i,j)!=blocNew)  {
					if(! (isChanged(i, j)==terrainAtPre.isChanged(i,j)))
						throw new PostCondException("post 8 moveObject");
				}
			}
		}
		
		for(int i=1; i<=getSizeX(); i++){
			for(int j=1; j<=getSizeY(); j++){
				if(getBloc(i,j)==bloc){
					terrainAtPre.getBloc(i, j).ChangeType(Type.Vide);
				}
				else if(getBloc(i,j)==blocNew){
					terrainAtPre.getBloc(i, j).ChangeType(typeAtPre);
				}
				if(! (getBloc(i,j).equals(terrainAtPre.getBloc(i, j))))
					throw new PostCondException("post 9 moveObject");
			}
		}
	}

}
